package util
{
	import flash.utils.ByteArray;
	
	
	/**
	 * com.adobe.crypto.MD5
	 * The MD5 Message-Digest Algorithm
	 *
	 * Implementation based on algorithm description at 
	 * [url]http://www.faqs.org/rfcs/rfc1321.html[/url]
	 */
	public class MD5 {
		
		/**
		 * Performs the MD5 hash algorithm on a string.
		 *
		 * @param s The string to hash
		 * @return A string containing the hash value of s
		 * @langversion ActionScript 3.0
		 * @playerversion Flash 9.0
		 * @tiptext
		 */
		public static function hash( s:String ):String {
			// initialize the md buffers
			var a:int = 1732584193;
			var b:int = -271733879;
			var c:int = -1732584194;
			var d:int = 271733878;
			
			// variables to store previous values
			var aa:int;
			var bb:int;
			var cc:int;
			var dd:int;
			
			// create the blocks from the string and
			// save the length as a local var to reduce
			// lookup in the loop below
			var x:Array = createBlocks( changeUTF(s) );
			var len:int = x.length;
			
			// loop over all of the blocks
			for ( var i:int = 0; i < len; i += 16) {
				// save previous values
				aa = a;
				bb = b;
				cc = c;
				dd = d;                                
				
				// Round 1
				a = ff( a, b, c, d, x[i+ 0],  7, -680876936 );         // 1
				d = ff( d, a, b, c, x[i+ 1], 12, -389564586 );        // 2
				c = ff( c, d, a, b, x[i+ 2], 17, 606105819 );         // 3
				b = ff( b, c, d, a, x[i+ 3], 22, -1044525330 );        // 4
				a = ff( a, b, c, d, x[i+ 4],  7, -176418897 );         // 5
				d = ff( d, a, b, c, x[i+ 5], 12, 1200080426 );         // 6
				c = ff( c, d, a, b, x[i+ 6], 17, -1473231341 );        // 7
				b = ff( b, c, d, a, x[i+ 7], 22, -45705983 );         // 8
				a = ff( a, b, c, d, x[i+ 8],  7, 1770035416 );         // 9
				d = ff( d, a, b, c, x[i+ 9], 12, -1958414417 );        // 10
				c = ff( c, d, a, b, x[i+10], 17, -42063 );                 // 11
				b = ff( b, c, d, a, x[i+11], 22, -1990404162 );        // 12
				a = ff( a, b, c, d, x[i+12],  7, 1804603682 );         // 13
				d = ff( d, a, b, c, x[i+13], 12, -40341101 );         // 14
				c = ff( c, d, a, b, x[i+14], 17, -1502002290 );        // 15
				b = ff( b, c, d, a, x[i+15], 22, 1236535329 );         // 16
				
				// Round 2
				a = gg( a, b, c, d, x[i+ 1],  5, -165796510 );         // 17
				d = gg( d, a, b, c, x[i+ 6],  9, -1069501632 );        // 18
				c = gg( c, d, a, b, x[i+11], 14, 643717713 );         // 19
				b = gg( b, c, d, a, x[i+ 0], 20, -373897302 );         // 20
				a = gg( a, b, c, d, x[i+ 5],  5, -701558691 );         // 21
				d = gg( d, a, b, c, x[i+10],  9, 38016083 );         // 22
				c = gg( c, d, a, b, x[i+15], 14, -660478335 );         // 23
				b = gg( b, c, d, a, x[i+ 4], 20, -405537848 );         // 24
				a = gg( a, b, c, d, x[i+ 9],  5, 568446438 );         // 25
				d = gg( d, a, b, c, x[i+14],  9, -1019803690 );        // 26
				c = gg( c, d, a, b, x[i+ 3], 14, -187363961 );         // 27
				b = gg( b, c, d, a, x[i+ 8], 20, 1163531501 );         // 28
				a = gg( a, b, c, d, x[i+13],  5, -1444681467 );        // 29
				d = gg( d, a, b, c, x[i+ 2],  9, -51403784 );         // 30
				c = gg( c, d, a, b, x[i+ 7], 14, 1735328473 );         // 31
				b = gg( b, c, d, a, x[i+12], 20, -1926607734 );        // 32
				
				// Round 3
				a = hh( a, b, c, d, x[i+ 5],  4, -378558 );         // 33
				d = hh( d, a, b, c, x[i+ 8], 11, -2022574463 );        // 34
				c = hh( c, d, a, b, x[i+11], 16, 1839030562 );         // 35
				b = hh( b, c, d, a, x[i+14], 23, -35309556 );         // 36
				a = hh( a, b, c, d, x[i+ 1],  4, -1530992060 );        // 37
				d = hh( d, a, b, c, x[i+ 4], 11, 1272893353 );         // 38
				c = hh( c, d, a, b, x[i+ 7], 16, -155497632 );         // 39
				b = hh( b, c, d, a, x[i+10], 23, -1094730640 );        // 40
				a = hh( a, b, c, d, x[i+13],  4, 681279174 );         // 41
				d = hh( d, a, b, c, x[i+ 0], 11, -358537222 );         // 42
				c = hh( c, d, a, b, x[i+ 3], 16, -722521979 );         // 43
				b = hh( b, c, d, a, x[i+ 6], 23, 76029189 );         // 44
				a = hh( a, b, c, d, x[i+ 9],  4, -640364487 );         // 45
				d = hh( d, a, b, c, x[i+12], 11, -421815835 );         // 46
				c = hh( c, d, a, b, x[i+15], 16, 530742520 );         // 47
				b = hh( b, c, d, a, x[i+ 2], 23, -995338651 );         // 48
				
				// Round 4
				a = ii( a, b, c, d, x[i+ 0],  6, -198630844 );         // 49
				d = ii( d, a, b, c, x[i+ 7], 10, 1126891415 );         // 50
				c = ii( c, d, a, b, x[i+14], 15, -1416354905 );        // 51
				b = ii( b, c, d, a, x[i+ 5], 21, -57434055 );         // 52
				a = ii( a, b, c, d, x[i+12],  6, 1700485571 );         // 53
				d = ii( d, a, b, c, x[i+ 3], 10, -1894986606 );        // 54
				c = ii( c, d, a, b, x[i+10], 15, -1051523 );         // 55
				b = ii( b, c, d, a, x[i+ 1], 21, -2054922799 );        // 56
				a = ii( a, b, c, d, x[i+ 8],  6, 1873313359 );         // 57
				d = ii( d, a, b, c, x[i+15], 10, -30611744 );         // 58
				c = ii( c, d, a, b, x[i+ 6], 15, -1560198380 );        // 59
				b = ii( b, c, d, a, x[i+13], 21, 1309151649 );         // 60
				a = ii( a, b, c, d, x[i+ 4],  6, -145523070 );         // 61
				d = ii( d, a, b, c, x[i+11], 10, -1120210379 );        // 62
				c = ii( c, d, a, b, x[i+ 2], 15, 718787259 );         // 63
				b = ii( b, c, d, a, x[i+ 9], 21, -343485551 );         // 64
				
				a += aa;
				b += bb;
				c += cc;
				d += dd;
			}
			
			// Finish up by concatening the buffers with their hex output
			return IntUtil.toHex( a ) + IntUtil.toHex( b ) + IntUtil.toHex( c ) + IntUtil.toHex( d );
		}
		
		/**
		 * Auxiliary function f as defined in RFC
		 */
		private static function f( x:int, y:int, z:int ):int {
			return ( x & y ) | ( (~x) & z );
		}
		
		/**
		 * Auxiliary function g as defined in RFC
		 */
		private static function g( x:int, y:int, z:int ):int {
			return ( x & z ) | ( y & (~z) );
		}
		
		/**
		 * Auxiliary function h as defined in RFC
		 */
		private static function h( x:int, y:int, z:int ):int {
			return x ^ y ^ z;
		}
		
		/**
		 * Auxiliary function i as defined in RFC
		 */
		private static function i( x:int, y:int, z:int ):int {
			return y ^ ( x | (~z) );
		}
		
		/**
		 * A generic transformation function.  The logic of ff, gg, hh, and
		 * ii are all the same, minus the function used, so pull that logic
		 * out and simplify the method bodies for the transoformation functions.
		 */
		private static function transform( func:Function, a:int, b:int, c:int, d:int, x:int, s:int, t:int):int {
			var tmp:int = a + int( func( b, c, d ) ) + x + t;
			return IntUtil.rol( tmp, s ) +  b;
		}
		
		/**
		 * ff transformation function
		 */
		private static function ff ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
			return transform( f, a, b, c, d, x, s, t );
		}
		
		/**
		 * gg transformation function
		 */
		private static function gg ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
			return transform( g, a, b, c, d, x, s, t );
		}
		
		/**
		 * hh transformation function
		 */
		private static function hh ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
			return transform( h, a, b, c, d, x, s, t );
		}
		
		/**
		 * ii transformation function
		 */
		private static function ii ( a:int, b:int, c:int, d:int, x:int, s:int, t:int ):int {
			return transform( i, a, b, c, d, x, s, t );
		}
		
		/**
		 * Converts a string to a sequence of 16-word blocks
		 * that we'll do the processing on.  Appends padding
		 * and length in the process.
		 *
		 * @param s The string to split into blocks
		 * @return An array containing the blocks that s was
		 *                        split into.
		 */
		private static function createBlocks( s:String ):Array {
			var blocks:Array = new Array();
			var len:int = s.length * 8;
			var mask:int = 0xFF; // ignore hi byte of characters > 0xFF
			for( var i:int = 0; i < len; i += 8 ) {
				blocks[ i >> 5 ] |= ( s.charCodeAt( i / 8 ) & mask ) << ( i % 32 );
			}
			
			// append padding and length
			blocks[ len >> 5 ] |= 0x80 << ( len % 32 );
			blocks[ ( ( ( len + 64 ) >>> 9 ) << 4 ) + 14 ] = len;
			return blocks;
		}
		
		[b]/**
		*add by alva sun 2007-06-15, warnning s.length must less than 65536
		*/
		private static function changeUTF2ASCII(s : String) : String{
			if(s.length > 65535) throw new Error("In MD5:changeUTF2 s.length must less than 65536");
			var ba : ByteArray = new ByteArray();
			ba.writeUTF(s);
			ba.position = 2;
			var rtns : String = "";
			for(var i:int=0, n : int = ba.bytesAvailable; i<n; i++){
				var un : int = ba.readUnsignedByte();
				var uns : String = String.fromCharCode(un);
				rtns += uns;
			}
			return rtns;
		}                
		
		private static function changeUTF(s : String) : String{
			var MAX_LEN : int = 65530;// must less than 65536
			var arrlen : int = Math.ceil(s.length/MAX_LEN);
			var tmps : String = "";
			var rtns : String = "";
			for(var i:int = 0; i< arrlen; i++){
				if(i == arrlen-1){
					tmps = s.substr(i*MAX_LEN);
				}else{
					tmps = s.substr(i*MAX_LEN, MAX_LEN);
				}
				rtns += changeUTF2ASCII(tmps);
			}        
			return rtns;
		}
	}
}
